package com.heima.schedule.service;

import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ScheduleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @author zzy
 * @date 2022/7/18 17:40
 */

@Service
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskinfoService taskinfoService;
    @Autowired
    private TaskinfoLogsService taskinfoLogsService;
    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        if (task == null) {
            log.warn("入参不能为空");
            return null;
        }

        //添加任务到task

        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(task, taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));

        boolean taskResult = taskinfoService.save(taskinfo);
        if (!taskResult) {
            log.warn("添加任务失败");
            throw new RuntimeException("任务添加失败");
        }

        //添加任务到log
        TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
        BeanUtils.copyProperties(task, taskinfoLogs);
        taskinfoLogs.setExecuteTime(new Date(task.getExecuteTime()));
        taskinfoLogs.setStatus(0);

        boolean logResult = taskinfoLogsService.save(taskinfoLogs);
        if (!logResult) {
            log.warn("添加日志失败");
            throw new RuntimeException("日志添加失败");
        }

        //判断任务执行时间是否小于当前时间
        //任务执行时间
        long executeTime = task.getExecuteTime();
        //当前时间
        long currentTime = System.currentTimeMillis();
        if (executeTime <= currentTime) {
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
        }

        //判断任务执行之间是否小于未来五分钟
        long futureTime = currentTime + (5 * 60 * 1000);
        if (executeTime <= currentTime) {
            cacheService.zAdd("FUTURE", JSON.toJSONString(task), executeTime);
        }
        //返回任务id
        return taskinfo.getTaskId();
    }

    //取消任务
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean cancelTask(Long taskId) {
        if (taskId == null) {
            log.warn("入参为空");
            return false;
        }
        //1.删除task_info
        //查询任务记录
        TaskinfoLogs taskinfoLogs = taskinfoLogsService.getById(taskId);

        //2.更新task_info_log
        taskinfoLogs.setStatus(ScheduleConstants.CANCELLED);
        boolean updateResult = taskinfoLogsService.updateById(taskinfoLogs);
        if (!updateResult) {
            log.warn("更新失败");
            throw new RuntimeException("任务记录更新失败");
        }
        //3.获得task对象
        Task task = new Task();
        BeanUtils.copyProperties(taskinfoLogs, task);
        task.setExecuteTime(taskinfoLogs.getExecuteTime().getTime());
        //4.cacheService lremove
        cacheService.lRemove("TOPIC", 0, JSON.toJSONString(task));
        //cacheService zRremove
        cacheService.zRemove("FUTURE", JSON.toJSONString(task));
        return true;
    }

    //消费任务
    @Override
    @Transactional(rollbackFor = RuntimeException.class)

    //todo
    public Boolean pollTask() {
        String taskResult = cacheService.lRightPop("TOPIC");
        if (StringUtils.isEmpty(taskResult)) {
            log.warn("没有任务");

        }
        //1.指定topic队列，进行消费，执行pop弹出方法 task结构的JSON字符串
        //2.把上一步的JSON字符串转成Task对象 JSON.parseObject（字符串，Task.class）  出参task对象
        Task task = JSON.parseObject(taskResult, Task.class);
        if (task == null) {
            log.warn("没有可执行任务");
            return false;
        }

        //3.taskinfoservice removebyID
        boolean taskInfoResult = taskinfoService.removeById(task.getTaskId());
        if (!taskInfoResult){
            log.warn("删除失败");
            return false;
        }
        //4.taskinfoService,updateById
        long executeTime = task.getExecuteTime();
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(logs,task);
        logs.setExecuteTime(new Date(executeTime));
        logs.setStatus(1);

        boolean update = taskinfoLogsService.updateById(logs);
        if (!update){
            log.warn("更新失败");
            throw new RuntimeException("更新失败");
        }
        return true;
    }

    //定时刷新任务
    @Scheduled(fixedRate = 1000 * 60 * 5)
    public void refresh(){
        log.info("任务被执行");
        String lock = cacheService.tryLock("refresh_lock", 4000);
        if (StringUtils.isEmpty(lock)){
            return;
        }
        Set<String> future = cacheService.zRangeByScore("FUTURE", 0, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(future)){
            log.warn("没有执行任务");
            return;
        }
        cacheService.refreshWithPipeline("FUTURE","TOPIC",future);
    }

    //初始化队列数据
    @Scheduled(fixedRate = 1000 * 60 * 5)
    public void initDate() {
        //清除缓存
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");
        //读取MySQL任务  查询taskInfo
        List<Taskinfo> taskinfoList = taskinfoService.list();
        if (CollectionUtils.isEmpty(taskinfoList)) {
            log.warn("查询失败");
            return;
        }
        for (Taskinfo taskinfo : taskinfoList) {
            if (taskinfo == null) {
                continue;
            }
            long time = taskinfo.getExecuteTime().getTime();
            long currentTimeMillis = System.currentTimeMillis();
            long future = currentTimeMillis + (1000 * 60 * 5);

            Task task = new Task();
            BeanUtils.copyProperties(taskinfo, task);
            task.setExecuteTime(time);
            //执行队列 任务时间是否小于当前时间
            if (time <= currentTimeMillis) {
                cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
            }
            //未来队列 执行时间是否小于未来时间
            if (time <= future) {
                cacheService.zAdd("FUTURE", JSON.toJSONString(task), time);
            }

        }
    }
}
